using System;
using System.IO;
using System.Xml;
using System.Security;
using System.Reflection;
using System.Collections;
using System.Configuration;
using System.ComponentModel;
using System.Security.Policy;
using System.Collections.Generic;
using System.Security.Permissions;
using System.Collections.ObjectModel;

namespace Irc.Net
{
	/// <summary>
	/// Description of PluginManager.
	/// </summary>
	public sealed class PluginManager : IDisposable, IEnumerable<Plugin>
	{
		private static PluginManager instance = new PluginManager();
		public static PluginManager Instance {
			get { return instance; }
		}
		public event EventHandler<PluginEventArgs> PluginLoading;
		private void OnPluginLoading(Assembly plugin)
		{
			PluginEventArgs e = new PluginEventArgs();
			e.Plugin = plugin;
			if(PluginLoading != null)
				PluginLoading(this, e);
			if(e.Cancel == false)
				PluginDomain.Load(plugin.GetName(false));
		}
		public Collection<Plugin> Plugins {
			get { return new Collection<Plugin>(pluginInstances); }
		}

		internal List<Plugin> pluginInstances = new List<Plugin>();
		private AppDomain PluginDomain;
		public PluginManager()
		{
			PermissionSet permSet = new PermissionSet(PermissionState.None);

			permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
			permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.ControlAppDomain));
			permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter));
			permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode));
			permSet.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.ReflectionEmit));
			permSet.AddPermission(new EnvironmentPermission(PermissionState.None));
			permSet.AddPermission(new FileDialogPermission(FileDialogPermissionAccess.OpenSave));
			permSet.AddPermission(new RegistryPermission(PermissionState.None));
			permSet.AddPermission(new UIPermission(PermissionState.Unrestricted));
			permSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.AllAccess,
								  Environment.CurrentDirectory + Path.DirectorySeparatorChar +
			                      ConfigurationManager.AppSettings["PluginPath"] + Path.DirectorySeparatorChar));

			AppDomainSetup setup = new AppDomainSetup();
			setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory + SettingsManager.Instance["PluginPath"] + Path.DirectorySeparatorChar;
			setup.DisallowApplicationBaseProbing = false;
			// Mono apparently doesn't support permissions and strong naming either.
			//PluginDomain = AppDomain.CreateDomain("IRC.NET Plugin Domain", AppDomain.CurrentDomain.Evidence, setup, permSet, CreateStrongName(Assembly.GetExecutingAssembly()));
			PluginDomain = AppDomain.CreateDomain("IRC.NET Plugin Domain", AppDomain.CurrentDomain.Evidence, setup);
		}
		public void EnablePlugins(System.Windows.Forms.Form form)
		{
			DirectoryInfo dir = new DirectoryInfo(Environment.CurrentDirectory + Path.DirectorySeparatorChar + SettingsManager.Instance["PluginPath"]);
			List<FileInfo> possiblePlugins = new List<FileInfo>(dir.GetFiles("*.dll"));
			foreach(FileInfo plugin in possiblePlugins)
			{
				Assembly assembly = Assembly.LoadFile(plugin.FullName);
				OnPluginLoading(assembly);
			}

			foreach(Assembly plugin in PluginDomain.GetAssemblies())
			{
				object[] attributes = plugin.GetCustomAttributes(true);
				if(attributes == null)
					continue;
				foreach(object attr in attributes)
				{
					PluginAttribute attribute = attr as PluginAttribute;
					if(attribute != null)
					{
						Plugin classObject = plugin.CreateInstance(attribute.PluginType.FullName) as Plugin;
						if(classObject.Initialize(form))
							pluginInstances.Add(classObject);
						else
							classObject.Dispose();
					}
				}
			}
		}		public void Dispose()
		{
			foreach(Plugin plugin in pluginInstances)
			{
				plugin.Dispose();
			}
		}
/*		private static StrongName CreateStrongName(Assembly assembly)
		{
			if(assembly == null)
				throw new ArgumentNullException("assembly");
			
			AssemblyName assemblyName = assembly.GetName();
			
			// get the public key blob
			byte[] publicKey = assemblyName.GetPublicKey();
			if(publicKey == null || publicKey.Length == 0)
				throw new InvalidOperationException("Assembly is not strongly named");
			
			StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);
			
			// and create the StrongName
			return new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
		}
*/
		public IEnumerator<Plugin> GetEnumerator()
		{
			return new PluginEnumerator(this);
		}
		IEnumerator IEnumerable.GetEnumerator()
		{
			return new PluginEnumerator(this);
		}
	}
	public interface Plugin : IDisposable
	{
		bool Initialize(System.Windows.Forms.Form mainWindow);
	}
	public class PluginEnumerator : IEnumerator<Plugin>, IEnumerator
	{
		private PluginManager instance;
		private int current;
		public PluginEnumerator(PluginManager instance) {
			this.instance = instance;
		}
		public Plugin Current {
			get { return instance.pluginInstances[current]; }
		}
		public void Dispose() {}
		object IEnumerator.Current {
			get { return (object)Current; }
		}
		public bool MoveNext() {
			if(current < instance.pluginInstances.Count) current++;
			else return false;
			return true;
		}
		public void Reset() {
			current = 0;
		}
	}
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true, Inherited=true)]
	public class PluginAttribute : Attribute
	{
		private Type pluginType;
		public Type PluginType { get { return pluginType; } }
		public PluginAttribute(Type type) { pluginType = type; }
	}
	public class PluginEventArgs : CancelEventArgs
	{
		private Assembly plugin;
		public Assembly Plugin {
			get { return plugin; }
			set { plugin = value; }
		}
	}
}
